package GoPing

import (
	"context"
	"sync"
	"sync/atomic"
	"time"

	"gitee.com/liumou_site/logger"
)

// Start 啟動並發任務來檢測一組地址的連通性。
// 該方法並發地執行ping操作，並等待所有ping操作完成後處理結果。
func (c *Concurrency) Start() {
	if len(c.Addr) == 0 {
		logger.Error("地址列表為空")
		return
	}

	var wg sync.WaitGroup
	// 創建帶有超時機制的上下文，預設超時時間為30秒
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// 遍歷地址列表，對每個地址啟動一個goroutine進行ping檢測
	for _, ip := range c.Addr {
		wg.Add(1)
		go func(addr string) {
			defer wg.Done()
			c.ping(ctx, addr)
		}(ip)
	}

	// 等待所有goroutine完成，確保所有地址的ping檢測都已完成
	wg.Wait()
	// 處理檢測結果
	c.handleResults()
}

// handleResults 處理並發情況下的IP地址檢查結果。
// 該方法遍歷IP地址列表，並更新成功和失敗的計數。
func (c *Concurrency) handleResults() {
	// 初始化結果映射（如果尚未初始化）
	if c.Result == nil {
		c.Result = make(map[string]bool)
	}

	// 遍歷IP地址列表
	for _, ip := range c.Addr {
		// 使用Load方法安全地檢查IP地址是否已在結果映射中
		if value, ok := c.Res.Load(ip); ok && value == "true" {
			// 如果IP地址存在於結果映射中並且值為"true"，表示處理成功
			c.Result[ip] = true
			atomic.AddInt32(&c.Success, 1)
		} else {
			// 如果IP地址不存在於結果映射中或值不為"true"，表示處理失敗
			c.Result[ip] = false
			atomic.AddInt32(&c.Fail, 1)
		}
		// 更新總處理數
		atomic.AddInt32(&c.Total, 1)
	}

	// 記錄檢測完成的信息
	logger.Info("檢測完成 - 總計: %d, 成功: %d, 失敗: %d", c.Total, c.Success, c.Fail)
}

// ping 使用並發方式對給定的IP地址執行ping操作。
// 該方法支持上下文控制，可以在超時或取消時提前退出。
// 參數:
//
//	ctx - 上下文，用於處理超時和取消。
//	ip - 需要進行ping操作的IP地址。
func (c *Concurrency) ping(ctx context.Context, ip string) {
	// 記錄ping操作開始的信息
	logger.Info("ping開始:", ip)

	// 創建一個完成通道，用於處理超時
	done := make(chan bool, 1)

	// 在單獨的goroutine中執行ping操作
	go func() {
		// 嘗試創建一個新的Ping實例
		p, err := New(ip, 5)
		if err != nil {
			logger.Error("創建ping實例失敗:", err)
			done <- false
			return
		}

		// 設置不打印詳細信息
		p.Print = false

		// 確保資源最終被釋放
		defer func() {
			if p.Conn != nil {
				if err := p.Close(); err != nil {
					logger.Error("關閉連接失敗:", err)
				}
			}
			done <- true
		}()

		// 對IP執行ping操作
		err = p.Ping(3) // 降低ping次數以提高效率
		if err != nil {
			logger.Error("ping失敗:", ip, err)
			return
		}

		// 如果ping操作成功，記錄成功信息並標記該IP為可達
		logger.Info("ping成功:", ip)
		c.L.Lock()
		c.Res.Store(ip, "true")
		c.L.Unlock()
	}()

	// 等待ping操作完成或上下文取消/超時
	select {
	case <-done:
		// ping操作已完成
		return
	case <-ctx.Done():
		// 上下文已取消或超時
		logger.Error("ping操作超時或被取消:", ip)
		return
	}
}
